﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;

namespace grid
{
    /// <summary>
    /// Draws Hasse diagram of given lLattice on given Panel
    /// </summary>
    class DiagramPainter
    {
        private Graphics _Canvas;
        private Panel _DrawPanel;
        private List<NodeButton> _Buttons;

        private NodeButton _Head;
        private NodeButton _Tail;

        /// <summary>
        /// Default constructor
        /// </summary>
        /// <param name="DrawPanel"></param>
        /// <param name="lLattice"></param>
        public DiagramPainter(Panel DrawPanel, Lattice lLattice)
        {
            _Canvas = DrawPanel.CreateGraphics();
            _Canvas.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
            _DrawPanel = DrawPanel;
            _Buttons = new List<NodeButton>();

            foreach (LatticeNode lNode in lLattice.Nodes)
            {
                NodeButton newButton = new NodeButton(lNode);
                _Buttons.Add(newButton);
                if (lNode.Extent.SetEquals(lLattice.Objects))
                    _Head = newButton;
                if (lNode.Intent.SetEquals(lLattice.Attributes))
                    _Tail = newButton;
            }

            ComputeCoordinats();
            foreach (NodeButton nbNode in _Buttons)
            {
                nbNode.FormParentsAndChildrenList(_Buttons);
                DrawPanel.Controls.Add(nbNode.Btn);
            }
            Draw();
        }

        private void ComputeCoordinats()
        {
            int width = _DrawPanel.Width;
            int height = _DrawPanel.Height;
            const int INDENT = 90;

            _Head.Top = INDENT;
            _Head.Left = width / 2;
            int SHIFT;
            if (_Buttons.Count() == 1)
                SHIFT = 0;
            else
                SHIFT = (height - 2 * INDENT) / (_Buttons.Count() - 1);
            UpdateCoordinats(_Head, SHIFT, INDENT);
        }

        private void UpdateCoordinats(NodeButton btn, int SHIFT, int INDENT)
        {
            int number_of_child = btn.Children.Count();
            int dist;
            int last_left;
            if (number_of_child == 1)
            {
                dist = (_DrawPanel.Width - 2 * INDENT) / 2;
                last_left = INDENT;
            }
            else
            {
                dist = (_DrawPanel.Width - 2 * INDENT) / (number_of_child - 1);
                last_left = INDENT - dist;
            }

            foreach (NodeButton child in _Buttons)
            {
                if(btn.HasChild(child))
                {
                    child.Top = btn.Top + SHIFT;
                    child.Left = last_left + dist;
                    last_left = child.Left;

                    UpdateCoordinats(child, SHIFT, INDENT);
                }
            }
        }

        public void Draw()
        {
            _Canvas = _DrawPanel.CreateGraphics();
            _Canvas.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

            Pen blackPen = new Pen(Color.Black, 2);
            foreach (NodeButton node in _Buttons)
            {
                foreach (NodeButton p in _Buttons)
                {
                    if(node.HasParent(p))
                    {
                        //TODO: Can done more efficient
                        Point anchor = new Point();
                        anchor.X = node.Left + node.Btn.Size.Width / 2;
                        anchor.Y = node.Top + node.Btn.Size.Height / 2;
                        Point anchorP = new Point();
                        anchorP.X = p.Left+ p.Btn.Size.Width / 2;
                        anchorP.Y = p.Top + p.Btn.Size.Height / 2;
                        _Canvas.DrawLine(blackPen, anchor, anchorP);
                    }
                }
            }
        }
    }

    /// <summary>
    /// NodeButton represents each lattice node on the canvas
    /// </summary>
    class NodeButton : LatticeNode
    {
        private bool isDragging;
        private int oldX, oldY;
        private Button _btn;

        protected List<NodeButton> _nbParents;
        protected List<NodeButton> _nbChildren;

        public List<NodeButton> nbParents { get { return _nbParents; } }
        public List<NodeButton> nbChildren { get { return _nbChildren; } }

        public Button Btn { get { return _btn; } }

        internal NodeButton(LatticeNode Node) : base()
        {
            _Extent = Node.Extent;
            _Intent = Node.Intent;
            _Parents = Node.Parents;
            _Children = Node.Children;

            _btn = new Button();
            _btn.Size = new Size(75, 23);
            _btn.Text = ToString();
            _btn.FlatStyle = FlatStyle.Flat;
            _btn.AutoSize = true;

            //Actions bindings
            _btn.MouseMove += new MouseEventHandler(btn_MouseMove);
            _btn.MouseDown += new MouseEventHandler(btn_MouseDown);
            _btn.MouseUp += new MouseEventHandler(btn_MouseUp);
        }

        internal void FormParentsAndChildrenList(List<NodeButton> buttons)
        {
            _nbParents = new List<NodeButton>(buttons.Where<NodeButton>(HasParent));
            _nbChildren = new List<NodeButton>(buttons.Where<NodeButton>(HasChild));
        }

        public bool HasParent(NodeButton node)
        {
            foreach (LatticeNode pNode in Parents)
            {
                if (node.Extent.SetEquals(pNode.Extent) &
                    node.Intent.SetEquals(pNode.Intent))
                    return true;               
            }
            return false;
        }

        public bool HasChild(NodeButton node)
        {
            foreach (LatticeNode cNode in Children)
            {
                if (node.Extent.SetEquals(cNode.Extent) &
                    node.Intent.SetEquals(cNode.Intent))
                    return true;
            }
            return false;
        }

        public int Top { get { return _btn.Top; } set { _btn.Top = value; } }
        public int Left { get { return _btn.Left; } set { _btn.Left = value; } }

        private void btn_MouseDown(object sender, MouseEventArgs e)
        {
            isDragging = true;
            oldX = e.X;
            oldY = e.Y;
        }

        private void btn_MouseMove(object sender, MouseEventArgs e)
        {
            if (isDragging)
            {
                _btn.Top = _btn.Top + e.Y - oldY;
                _btn.Left = _btn.Left + e.X - oldX;
                if (CheckOrderFail())
                    Top = _btn.Top - e.Y + oldY;
                _btn.TopLevelControl.Refresh();
            }

        }

        private void btn_MouseUp(object sender, MouseEventArgs e)
        {
            isDragging = false;
        }

        public override string ToString()
        {
            //TODO: Short labels
            return String.Concat(Extent.ToArray<string>()) + " : " +
                String.Concat(Intent.ToArray<string>());
        }

        /// <summary>
        /// Checks Node not to be above it's parent
        /// </summary>
        private bool CheckOrderFail()
        {
            foreach (NodeButton pNode in nbParents)
            {
                if (pNode.Top > Top)
                    return true;
            }

            foreach (NodeButton cNode in nbChildren)
            {
                if (cNode.Top < Top)
                    return true;
            }

            return false;
        }
    }
}
